#ifndef SCENE_H
#define SCENE_H

#include "stdafx.h"
#include "SceneObj.h"
#include "RenderInterface.h"
#include "Camera.h"
#include "NavierStokesSolver.h"
#include "ParticleSys.h"
#include "VoxelCollisionDetector.h"
#include <vector>
using namespace std;

class DecoSceneNode;
class DecoScene;
class DecoArticulatedObject;

typedef unsigned long DWORD;

class DecoZone
{
protected:
	BYTE zoneID;
	BOOL bIsVisible;
public:
	DecoZone();
	virtual ~DecoZone();
	void Render(DecoRenderInterface* RI, DecoCamera* camera);
};



class DecoSceneNode
{
private:
	BOOL bIsLeaf;
	DecoSceneNode* Parent;
	DecoSceneNode* leftChild;
	DecoSceneNode* rightChild;
	vector<DecoSceneObject*> sceneObjs;
	vector<DecoLight*> sceneLights;
	DecoScene* scene;
	void RenderAllSceneObjects(DecoRenderInterface* RI, DecoCamera* camera, SceneObjectType objType = AllT, DecoDrawType drawType = DT_SolidPolygon);
public:
	DecoSceneNode(DecoScene* scn, BOOL isLeaf, DecoSceneNode* lchild = NULL, DecoSceneNode* rchild = NULL);
	~DecoSceneNode();
	void Render(DecoRenderInterface* RI, DecoCamera* camera, SceneObjectType objType = AllT, DecoDrawType drawType = DT_SolidPolygon);
	void GetRelevantLights(const DecoRenderData* obj, INT& numLights, DecoLight** lights);
	DecoSceneObject* GetFirstSceneObj()
	{
		if (sceneObjs.size())
			return sceneObjs[0];
		else
			return NULL;
	}
	vector<DecoSceneObject*>& GetSceneObjects()
	{
		return sceneObjs;
	};
	friend DecoArchive& operator<< (DecoArchive& Ar, const DecoSceneNode& node);
	friend DecoArchive& operator>> (DecoArchive& Ar, DecoSceneNode& node);
};

class DecoSolidObject;
class GridField;
class TetGridField;

class DecoScene
{
private:
	DecoSceneNode* rootSceneNode;
	DOUBLE timeStep;
	vector<DecoSceneObject*> selectedObjs;
	DWORD* tmpVoxelizerBufferX;
	BYTE*** solidElements;
	Box scnBox;
	UINT numXElement;
	UINT numYElement;
	UINT numZElement;
	DOUBLE stepSize;
	INT numXElementVoxelize;
	INT numYElementVoxelize;
	INT numZElementVoxelize;
	DecoColor* buffer;
	DOUBLE time;
	VoxelCollisionDetector mCollisionDetector;
	vector3 PointScreen2Camera(const vector2& pt) const;
	void sliceRender(DecoRenderData* obj, BYTE*** solidElements, INT numSlices, INT height, INT width, INT id = 0);
public:
	DecoRenderInterface* RI;
	DecoCamera* camera;
	NSSolver solver;
	GridField grids;

	vector<double> mEnergy;
	vector<double> mTorqueAbs;
	vector<double> mTorqueSqr;

	vector3 mvBodyForces[10];
	vector3 mvTorque;
	vector3 mvCOMs[10];
	vector<vector4> mvTipTrace;
	vector<vector4> mvTipTraceBack;

#ifdef _PARTICLES
	ParticleSys particles;
#endif
	//static DecoScene* GetSingleton();
	//static void DestroySingleton();
	DecoScene();
	~DecoScene();
	void Render(BOOL bViewModel = TRUE);
	void Reshape(int w, int h)
	{
		assert(RI);
		RI->SetViewport (0, 0, (GLsizei) w, (GLsizei) h);
	}
	void DumpSolidRib(const string& filename);
	void Voxelize();
	void CalcForces();
	void UpdateSolid();
	void UpdateSolidTransformation(bool bToTarget = false);
	void UpdateSolidBeforeCoupling();
	void VoxelizeSprayer();
	void VoxelizeSolid();
	void VoxelizeFluid();
	// next two functions should be set when initialize in sequence
	void SetSceneBox(const Box& _scnBox)
	{
		scnBox = _scnBox;
		grids.SetSceneBox(scnBox);

	}
	void SetGridsInformation();
	void SetNumXElement (UINT num);
	void AllocateTempBuffer();
	GridField* GetGrid()
	{
		return &grids;
	}
	//////////////////////////////////////////////////
	Box GetSceneBox() const
	{
		return scnBox;
	}
	DOUBLE GetTimeStep() const
	{
		return timeStep;
	}
	UINT GetNumXElement () const
	{
		return numXElement;
	}
	UINT GetNumYElement () const
	{
		return numYElement;
	}
	UINT GetNumZElement () const
	{
		return numZElement;
	}
	DOUBLE GetSpaceStepSize() const
	{
		return stepSize;
	}
	DecoSceneObject* GetFirstObject()const;
#ifdef _DENSITY
	void RenderVolume(BOOL bViewModel = TRUE);
#endif
	void DumpSolidGrid();
	void RenderGridField();
	void PointScreen2WorldRay(const vector2& pt, vector3& outOrigin, vector3& outDir) const;
	vector3 vectorScreen2World(const vector2& start, const vector2& end, DOUBLE startDistance = 0, Plane constraint[] = NULL, INT numConstraints = 0) const;
	DecoSceneObject* SelectObj(FLOAT x, FLOAT y) const; 
	//DecoSceneObject* MouseOnSelectedObj(FLOAT x, FLOAT y) const;
	//DecoSceneObject* MouseClickOnBlank(FLOAT x, FLOAT y) const;
	void DeselectAll ();
	BOOL IsSomethingSelected() const;
	// return FALSE if there is no intersection
	void Destroy(DecoSceneObject* obj);
	void DestroyAll();
	//erase from list but deletion is left to decoObjFactory
	BOOL EraseAllSceneObject();
	DecoSceneObject* GetFocusObject()const;
	void SetFocusObject(DecoSceneObject* obj);
	void AddSceneObject(DecoSceneObject* solid);
	void PrepareSolidJacobian();
	void DumpObjFiles(const std::string& fileName);
	DOUBLE OptimizeArticulatedQDot(DOUBLE seconds);
	void GatherAndOutputStatistics(int ithSample1, int numSample1, double p1, int ithSample2, int numSample2, double p2);
#ifdef _SLC
	void TransformObjectToFluid(const string& objName);
#endif
	void ToggleShowGrid();
	void Update(DOUBLE seconds);
	DOUBLE GetCurrentTime();
	void Reset();
	void ResetCurrentTime();
	DecoArticulatedObject* GetArticulatedObject();
	DecoSceneObject* GetSceneObj(const char* name);
	void GetAllFreers ( vector<DecoSceneObject*> &allFreeers );
	void GetAllSceneObjects( vector<DecoSceneObject*> &allObjs);
	double CollisionDetection();
	void Serialize(DecoArchive& Ar);
	void Deserialize(DecoArchive& Ar);

	static tbb::mutex msMutex;
#ifdef MFC_ENABLED
	BOOL LoadFromXML(const std::string& filepath);
	BOOL SaveToXML(const std::string& filepath);
#endif
};

#endif
